﻿using System;
using System.Collections.Generic;
using System.Text;

namespace MLForgeSharp.Models.UnsupervisedLearningModels.NeuralNet
{
    /// <summary>
    /// 自组织映射网SOM
    /// </summary>
    public class KohonenNet
    {
        private double[,] weights;
        private int inputSize;
        private int outputSize;
        private double learningRate;
        private double decayRate;

        public KohonenNet(int inputSize, int outputSize, double learningRate, double decayRate)
        {
            this.inputSize = inputSize;
            this.outputSize = outputSize;
            this.learningRate = learningRate;
            this.decayRate = decayRate;

            // Initialize weights randomly
            weights = new double[outputSize, inputSize];
            Random rand = new Random();
            for (int i = 0; i < outputSize; i++)
            {
                for (int j = 0; j < inputSize; j++)
                {
                    weights[i, j] = rand.NextDouble();
                }
            }
        }

        public int Train(double[] input)
        {
            // Find the best matching unit (BMU)
            int bmuIndex = 0;
            double minDistance = double.MaxValue;

            for (int i = 0; i < outputSize; i++)
            {
                double distance = 0;
                for (int j = 0; j < inputSize; j++)
                {
                    distance += Math.Pow(input[j] - weights[i, j], 2);
                }
                if (distance < minDistance)
                {
                    minDistance = distance;
                    bmuIndex = i;
                }
            }

            // Update weights
            for (int i = 0; i < outputSize; i++)
            {
                for (int j = 0; j < inputSize; j++)
                {
                    weights[i, j] += learningRate * (input[j] - weights[i, j]);
                }
            }

            // Decay the learning rate
            learningRate *= decayRate;

            return bmuIndex;
        }

        public int Predict(double[] input)
        {
            int bmuIndex = 0;
            double minDistance = double.MaxValue;

            for (int i = 0; i < outputSize; i++)
            {
                double distance = 0;
                for (int j = 0; j < inputSize; j++)
                {
                    distance += Math.Pow(input[j] - weights[i, j], 2);
                }
                if (distance < minDistance)
                {
                    minDistance = distance;
                    bmuIndex = i;
                }
            }

            return bmuIndex;
        }
    }
    public class KohonenNetExample
    {
        public KohonenNetExample()
        {
            // Example usage of KohonenNet
            int inputSize = 3;
            int outputSize = 4;
            double learningRate = 0.1;
            double decayRate = 0.99;

            KohonenNet kohonenNet = new KohonenNet(inputSize, outputSize, learningRate, decayRate);

            // Example input data
            double[][] inputs = new double[][]
        {
            new double[] { 0.1, 0.2, 0.3 },
            new double[] { 0.4, 0.5, 0.6 },
            new double[] { 0.7, 0.8, 0.9 },
            new double[] { 0.2, 0.3, 0.4 }
        };

            // Training the network
            for (int epoch = 0; epoch < 100; epoch++)
            {
                foreach (var input in inputs)
                {
                    kohonenNet.Train(input);
                }
            }

            // Predicting the cluster for a new input
            double[] newInput = new double[] { 0.3, 0.4, 0.5 };
            int cluster = kohonenNet.Predict(newInput);

            Console.WriteLine("The input belongs to cluster: {cluster}");
    }
}
}
